#include "drv_dmac.h"
#include "stdio.h"
#include "soc.h"
#include "debug_perf.h"
#include "flash_indirect_opt.h"
#include "dw_dmac.h"

#define DMA_M2M_SIZE (512 * 6 * 4) // 12K Bytes
static uint32_t dmac_transfer_times = 0;

volatile uint8_t dma_cb_flag = 1;
uint8_t *addr_dst = NULL;
uint8_t *addr_src = NULL;

static uint32_t dmac_irq_count = 0;
//static dmac_linked_t dmac_link[1024] = {0};
static dmac_linked_t *dmac_link = NULL;

static void dma_event_cb_fun(int32_t ch, dma_event_e event, void *arg)
{
    if (event == DMA_EVENT_TRANSFER_DONE)
    {
        // printf("*%d",dmac_irq_count);
        dmac_irq_count++;
        if (dmac_irq_count < dmac_transfer_times)
        {
            csi_dma_stop(ch);
            csi_dma_start(ch, addr_src, addr_dst + dmac_irq_count * DMA_M2M_SIZE, DMA_M2M_SIZE / 4);
        }
        else
        { 
            csi_dma_stop(ch);
            csi_dma_release_channel(ch);
            dma_cb_flag = 1;
			printf("w ok\n");
        }
    }
}

static void dma_linded_mode_event_cb_fun(int32_t ch, dma_event_e event, void *arg)
{
	// printf(":e%d", event);
    //printf("e%d", event);
	if (event == DMA_EVENT_TRANSFER_DONE)
    {
        // printf("*%d",dmac_irq_count);
        csi_dma_stop(ch);
        csi_dma_release_channel(ch);
        dma_cb_flag = 1;
		printf("w ok \n");
    }
}

static cd_qspi_ctrl_reg_t *qspi_ctrl_addr = (cd_qspi_ctrl_reg_t *)CSKY_QSPIC0_BASE;
int indirect_memcpy_dma(uint8_t *dest_addr, uint8_t *addr_offset, uint32_t len)
{
    uint32_t i;
    int32_t ch;
    dma_config_t config;
    uint32_t reg_temp = 0;
    uint32_t ret = 0;
	dma_cb_flag = 0;
    addr_dst = dest_addr;
    addr_src = addr_offset;

    ret = csi_dma_single_channel_choose(QSPIFLASH_DMA_CH);//dma0 channel-1
    if (ret) {
        return -1;
    }

    ch = csi_dma_alloc_channel();
    if (ch == -1) {
        return -2;
    }
    
    config.src_inc  = DMA_ADDR_INC;
    config.dst_inc  = DMA_ADDR_INC;
    config.src_endian = DMA_ADDR_LITTLE;
    config.dst_endian = DMA_ADDR_LITTLE;
    config.src_tw   = DMA_DATAWIDTH_SIZE32;
    config.dst_tw   = DMA_DATAWIDTH_SIZE32;
    config.group_len = 8;
    config.mode = DMA_BLOCK_TRIGGER;
    config.type     = DMA_MEM2MEM;
    config.ch_mode  = DMA_MODE_SOFTWARE;
    config.src_reload_en = 0;
    config.dest_reload_en = 0;

    ret = csi_dma_config_channel(ch, &config, dma_event_cb_fun, NULL);
    if (ret) {
        return -3;
    }

    qspi_ctrl_addr->INDIRECT_READ_TRANS_START_ADDR = (uint32_t)addr_offset;
    qspi_ctrl_addr->INDIRECT_READ_TRANS_NUM_BYTES = len;
    qspi_ctrl_addr->INDIRECT_AHB_ADDR_TRIGGER = (uint32_t)addr_offset;
    qspi_ctrl_addr->INDIRECT_TRIGGER_ADDR_RANGE = 0x0f;
    reg_temp = qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL;
    reg_temp |= (0x00000001);
    qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL = reg_temp; //start indirect transfer

    dma_cb_flag = 0;
	dmac_transfer_times = (len % DMA_M2M_SIZE) ? (len / DMA_M2M_SIZE) + 1 : (len / DMA_M2M_SIZE);
	printf("len:%d, times:%d\r\n", len, dmac_transfer_times);
	csi_dma_start(ch, addr_src, addr_dst, DMA_M2M_SIZE / 4);
//	printf("len:%d, times:%d\r\n", len, dmac_transfer_times);
    return 0;   
}

int indirect_memcpy_dma_linked(uint8_t *dest_addr, uint8_t *addr_offset, uint32_t len)
{
	uint32_t i;
	int32_t ch;
    dma_config_t config;
    uint32_t reg_temp = 0;
    uint32_t ret = 0;
	dma_cb_flag = 0;
	ret = csi_dma_single_channel_choose(QSPIFLASH_DMA_CH); //dma0 channel-1
	if (ret)
	{
		return -1;
	}

	ch = csi_dma_alloc_channel();
	if (ch == -1)
	{
		return -2;
	}

	config.src_inc = DMA_ADDR_INC;
	config.dst_inc  = DMA_ADDR_INC;
    config.src_endian = DMA_ADDR_LITTLE;
    config.dst_endian = DMA_ADDR_LITTLE;
    config.src_tw   = DMA_DATAWIDTH_SIZE32;
    config.dst_tw   = DMA_DATAWIDTH_SIZE32;
    config.group_len = 8;
    config.mode = DMA_BLOCK_TRIGGER;
    config.type     = DMA_MEM2MEM;
    config.ch_mode  = DMA_MODE_SOFTWARE;
    config.src_reload_en = 0;
    config.dest_reload_en = 0;

	ret = csi_dma_config_channel(ch, &config, dma_linded_mode_event_cb_fun, NULL);
	if (ret) {
        return -3;
    }

    qspi_ctrl_addr->INDIRECT_READ_TRANS_START_ADDR = (uint32_t)addr_offset;
    qspi_ctrl_addr->INDIRECT_READ_TRANS_NUM_BYTES = len;
    qspi_ctrl_addr->INDIRECT_AHB_ADDR_TRIGGER = (uint32_t)addr_offset;
    qspi_ctrl_addr->INDIRECT_TRIGGER_ADDR_RANGE = 0x0f;
    reg_temp = qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL;
    reg_temp |= (0x00000001);
    qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL = reg_temp; //start indirect transfer

    dma_cb_flag = 0;

	dmac_transfer_times = (len % DMA_M2M_SIZE) ? (len / DMA_M2M_SIZE) + 1 : (len / DMA_M2M_SIZE);
	// printf("len:%d,dmac_transfer_times:%d\r\n", len, dmac_transfer_times);
	dmac_link = (dmac_linked_t *)aos_malloc(1024 * sizeof(dmac_linked_t));
	if(NULL == dmac_link)
	{
		printf("mem malloc failed \n");
		return -1;
	}
    memset(dmac_link, 0, sizeof(dmac_link));
	for (uint32_t i = 0; i < dmac_transfer_times; i++)
	{
		dmac_link[i].src_addr = addr_offset;
		dmac_link[i].dst_addr = dest_addr + i * DMA_M2M_SIZE;
		dmac_link[i].llp = &dmac_link[i + 1];
		dmac_link[i].CTL_low = 0x1a804825;
		dmac_link[i].CTL_high = DMA_M2M_SIZE / 4; //3k*1024 Byte
	}
    // dmac_link[dmac_transfer_times - 1].CTL_low = 0x02804825;
    dmac_link[dmac_transfer_times - 1].llp = 0;

	csi_dcache_clean_invalid();
    *(uint32_t *)0xfff60318 = 0x100; //mask block irq
	*(uint32_t *)0xfff60018 = 0x1a804825; //set llp_src_en,llp_dst_en
	*(uint32_t *)0xfff60040 = 0x400c00;
	*(uint32_t *)0xfff60044 = 0x00;
	*(uint32_t *)0xfff60010 = dmac_link;
	*(uint32_t *)0xfff603a0 = 0x101;
	// while(*(uint32_t *)0xfff603a0 == 1);
    return 0;
}

void indirect_memcpy(uint8_t *dest_addr, uint8_t *addr_offset, uint32_t len)
{
	uint32_t reg_temp = 0;
	uint32_t opt_byte_num = 0;

	while (len)
	{
		opt_byte_num = (len > INDIRECT_MAX_SIZE) ? INDIRECT_MAX_SIZE : len;

		qspi_ctrl_addr->INDIRECT_READ_TRANS_START_ADDR = (uint32_t)addr_offset;
		qspi_ctrl_addr->INDIRECT_READ_TRANS_NUM_BYTES = opt_byte_num;
		qspi_ctrl_addr->INDIRECT_AHB_ADDR_TRIGGER = (uint32_t)addr_offset;
		qspi_ctrl_addr->INDIRECT_TRIGGER_ADDR_RANGE = AHB_ADDR_RANGE;

		reg_temp = qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL;
		reg_temp |= (0x00000001);
		qspi_ctrl_addr->INDIRECT_READ_TRANSFER_CONTROL = reg_temp; //start indirect transfer

		memcpy(dest_addr, (uint8_t *)addr_offset, opt_byte_num);

		len -= opt_byte_num;
		addr_offset += opt_byte_num;
		dest_addr += opt_byte_num;
	}
}

#if 1
int test_qspiflash_copy_dmac_mode(void)
{
    int ret = 0;
    printf("dmac test\r\n");

    DBG_PERF_INIT();
    ret = indirect_memcpy_dma_linked((uint8_t *)0x5000000, (uint8_t *)0xc0000000, 11 * 1024 * 1024);
    if (!ret)
    {
        while (!dma_cb_flag)
            ;
        DBG_PERF_PRINT();
        printf("dmac test complete\r\n");
    }
    return ret;
}
#endif
